use std::{convert::TryFrom, str::FromStr};

use crate::{
    errors::cons_error,
    middlewares::authorize::{club_getters::default_getter, Authorize},
    models::{
        blacklist::BlacklistContent,
        club_form::{ClubFormModelDTO, ClubFormType},
        user::{NormalUserModel, UserRole},
        user_application::UserApplicationModelDTO,
        ClubFormStatusFlowType,
    },
    services::{
        black_list_service::{BlacklistService, CheckInBlackListSvrReq},
        form_service::{CreateFormToClubReq, FormService},
    },
    utils::authorize::check_user_has_permission_with_object_id,
    AppError, AppResult, PaginationQuery,
};

use actix_web::{web, HttpResponse};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use wither::bson::{doc, oid::ObjectId};

use super::OK_RESPONSE;

mod ctrl_struct {
    use serde::{Deserialize, Serialize};
    use serde_json::Value;

    use crate::{
        models::{club_form::ClubFormType, ClubFormStatusFlow},
        services::form_service::{ApplyApplicationToFormReq, UpdateClubFormReq},
    };
    define_filter_request_struct!(SearchAllFormsReq {
        club_id:Option<String>
    });
    define_request_struct!(DeleteFormReq { form_id: String });
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct UpdateClubFormCtrlReq {
        pub form_id: String,
        #[serde(rename = "type")]
        pub form_type: Option<ClubFormType>,
        pub title: Option<String>,
        pub description: Option<String>,
        pub allow_repeat: Option<bool>,
        pub allow_rewrite: Option<bool>,
        pub layout: Option<Value>,
        pub rule: Option<Value>,
        pub status_flow: Option<ClubFormStatusFlow>,
        pub active: Option<bool>,
    }
    impl From<UpdateClubFormCtrlReq> for UpdateClubFormReq {
        fn from(req: UpdateClubFormCtrlReq) -> Self {
            Self {
                form_type: req.form_type,
                title: req.title,
                description: req.description,
                allow_repeat: req.allow_repeat,
                allow_rewrite: req.allow_rewrite,
                layout: req.layout,
                rule: req.rule,
                status_flow: req.status_flow,
                active: req.active,
            }
        }
    }
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct ApplyApplicationToFormCtrlReq {
        pub form_id: String,
        pub content: Value,
    }
    impl From<ApplyApplicationToFormCtrlReq> for ApplyApplicationToFormReq {
        fn from(req: ApplyApplicationToFormCtrlReq) -> Self {
            Self {
                content: req.content,
            }
        }
    }
    define_filter_request_struct!(SearchAllApplicationsInFormReq { form_id: String });
    define_request_struct!(FormDetailReq { form_id: String });
}

async fn search_all_forms(
    form_service: web::Data<FormService>,
    page_query: web::Query<PaginationQuery>,
    where_value: web::Json<ctrl_struct::SearchAllFormsReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    if !check_user_has_permission_with_object_id(user.clone(), None, UserRole::Admin) {
        match &where_value.club_id {
            None => {
                return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
            }
            Some(club_id) => {
                let oid = ObjectId::from_str(club_id)?;
                if !check_user_has_permission_with_object_id(user, oid, UserRole::LeaderOrAdmin) {
                    return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
                }
            }
        }
    }
    let res = form_service
        .find_all_forms(
            &page_query,
            where_value.get_filter_as_document_use_default(),
        )
        .await?;

    Ok(HttpResponse::Ok().json(res.map_into::<ClubFormModelDTO>()))
}

/// TODO: 增加对状态流的设置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateFormCtrlReq {
    pub club_id: String,
    #[serde(rename = "type")]
    pub form_type: ClubFormType,
    pub title: String,
    pub description: Option<String>,
    #[serde(default = "Default::default")]
    pub allow_repeat: bool,
    #[serde(default = "Default::default")]
    pub allow_rewrite: bool,
    pub rule: Option<Value>,
    pub layout: Value,
}

impl TryFrom<CreateFormCtrlReq> for CreateFormToClubReq {
    type Error = AppError;

    fn try_from(value: CreateFormCtrlReq) -> Result<Self, Self::Error> {
        Ok(CreateFormToClubReq {
            club_id: ObjectId::from_str(&value.club_id)?,
            form_type: value.form_type,
            title: value.title,
            description: value.description,
            allow_repeat: value.allow_repeat,
            allow_rewrite: value.allow_rewrite,
            rule: value.rule,
            layout: value.layout,
        })
    }
}

async fn create_form(
    form_service: web::Data<FormService>,
    req: web::Json<CreateFormCtrlReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let req = CreateFormToClubReq::try_from(req.into_inner())?;
    if !check_user_has_permission_with_object_id(user, req.club_id, UserRole::LeaderOrAdmin) {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = form_service.create_form_to_club(req).await?;
    Ok(HttpResponse::Ok().json(ClubFormModelDTO::from(res)))
}
async fn delete_form(
    form_service: web::Data<FormService>,
    req: web::Json<ctrl_struct::DeleteFormReq>,
) -> AppResult<HttpResponse> {
    let form_id = ObjectId::from_str(&req.form_id)?;
    form_service.delete_form(form_id).await?;
    Ok(HttpResponse::Ok().json(OK_RESPONSE))
}

async fn update_form(
    form_service: web::Data<FormService>,
    req: web::Json<ctrl_struct::UpdateClubFormCtrlReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let form_id = ObjectId::from_str(&req.form_id)?;
    let form_detail = form_service.form_detail(form_id).await?;
    if !check_user_has_permission_with_object_id(user, form_detail.club_id, UserRole::LeaderOrAdmin)
    {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = form_service
        .update_club_form(form_id, req.into_inner().into())
        .await?;
    Ok(HttpResponse::Ok().json(ClubFormModelDTO::from(res)))
}

/// ## apply_application_to_form
/// 提交一个指定的表单
async fn apply_application_to_form(
    user: NormalUserModel,
    form_service: web::Data<FormService>,
    b_svr: web::Data<BlacklistService>,
    req: web::Json<ctrl_struct::ApplyApplicationToFormCtrlReq>,
) -> AppResult<HttpResponse> {
    let form_id = ObjectId::from_str(&req.form_id)?;
    let form_detail = form_service.form_detail(form_id).await?;
    if !form_detail.valid || !form_detail.active {
        return Err(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND));
    }
    if b_svr
        .check_in_blacklist(CheckInBlackListSvrReq {
            target_id: user.id,
            content: BlacklistContent::Application {
                club_id: form_detail.club_id,
            },
        })
        .await?
    {
        return Err(AppError::no_permission(cons_error::MSG_IN_BLACK_LIST));
    }
    let res = form_service
        .apply_application_to_form(user.id, form_detail, req.into_inner())
        .await?;
    Ok(HttpResponse::Ok().json(UserApplicationModelDTO::from(res)))
}

async fn search_all_applications_in_form(
    form_service: web::Data<FormService>,
    user: NormalUserModel,
    page_query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::SearchAllApplicationsInFormReq>,
) -> AppResult<HttpResponse> {
    let form_id = ObjectId::from_str(&req.form_id)?;
    let form_detail = form_service.form_detail(form_id).await?;
    if !check_user_has_permission_with_object_id(user, form_detail.club_id, UserRole::LeaderOrAdmin)
    {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let mut where_value = req.get_filter_as_document_use_default().unwrap_or_default();
    where_value.insert("form_id", form_id);
    // 草稿状态的提交不被此接口查询
    where_value.insert(
        "status.type",
        doc! {"$not":{"$eq":ClubFormStatusFlowType::Draft}},
    );
    let res = form_service
        .find_all_application(page_query.into_inner(), where_value)
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<UserApplicationModelDTO>()))
}

async fn form_detail(
    svr: web::Data<FormService>,
    req: web::Json<ctrl_struct::FormDetailReq>,
) -> AppResult<HttpResponse> {
    let form_id = ObjectId::from_str(&req.form_id)?;
    let form_detail = svr.form_detail(form_id).await?;
    Ok(HttpResponse::Ok().json(ClubFormModelDTO::from(form_detail)))
}
pub fn configure_form_controller(config: &mut web::ServiceConfig) {
    config
        .route("/create", web::post().to(create_form))
        .service(
            web::resource("/delete")
                .route(web::post().to(delete_form))
                .wrap(Authorize::new(UserRole::Admin, default_getter)),
        )
        .route("/update", web::post().to(update_form))
        .route("/apply", web::post().to(apply_application_to_form))
        .route("/search", web::post().to(search_all_forms))
        .route("/detail", web::post().to(form_detail))
        .service(
            web::scope("/application")
                .route("search", web::post().to(search_all_applications_in_form)),
        );
}
